#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_device.h>

struct spi_vir_driver_data {
    struct spi_master *master;
    struct platform_device *pdev;
    struct work_struct ws;
    struct list_head queue;
    wait_queue_head_t wait;
    spinlock_t lock;
};

static int spi_vir_master_transfer(struct spi_device *spi, struct spi_message *mesg)
{
    unsigned long flags;
    struct spi_vir_driver_data *ss = spi_master_get_devdata(spi->master);

#if 0	
    /* 方法1: 直接实现spi传输 */
    /* 假装传输完成, 直接唤醒 */
    mesg->status = 0; // 设置传输完成标志位
    mesg->complete(mesg->context);
    return 0;
        
#else
    /* 方法2: 使用工作队列启动SPI传输、等待完成 */
    /* 把消息放入队列 */

    /* 加锁 */
    spin_lock_irqsave(&ss->lock, flags);

    mesg->actual_length = 0;
    mesg->status = -EINPROGRESS;

    /* 往消息队列中添加数据 */
    list_add_tail(&mesg->queue, &ss->queue);
    
    /* 启动工作队列 */
    schedule_work(&ss->ws);

    /* 解锁 */
    spin_unlock_irqrestore(&ss->lock, flags);
    
    /* 直接返回 */
    return 0;
#endif
}

static void spi_vir_master_work(struct work_struct *work)
{
    struct spi_vir_driver_data *ss = container_of(work, struct spi_vir_driver_data, ws);
    struct spi_message *mesg;
    unsigned long flags;

    spin_lock_irqsave(&ss->lock, flags);
	
	while (!list_empty(&ss->queue)) {
        // 获取mesg中加锁,临界区
		mesg = list_entry(ss->queue.next, struct spi_message, queue);
        
		list_del_init(&mesg->queue);

        spin_unlock_irqrestore(&ss->lock, flags);
		/* 假装硬件传输已经完成 */
        
        spin_lock_irqsave(&ss->lock, flags);
		mesg->status = 0;
		if (mesg->complete)
			mesg->complete(mesg->context);

	}

    spin_unlock_irqrestore(&ss->lock, flags);
    
    return;
}

static int spi_vir_master_probe(struct platform_device *pdev)
{
    struct spi_master *master = NULL;
    int ret = 0;
    struct spi_vir_driver_data *svdd = NULL;

    /* 分配/设置/注册spi_master */
    master = spi_alloc_master(&pdev->dev, sizeof(struct spi_vir_driver_data));
    if (master == NULL) {
        dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
		return -ENOMEM;
    }

    /* 设置私有数据 */
    svdd = spi_master_get_devdata(master);
    platform_set_drvdata(pdev, svdd);
    svdd->master = master;
    svdd->pdev = pdev;

    /* 设置master设备树 */
    master->dev.of_node = pdev->dev.of_node;

    /* Old spi master中的spi_sync使用transfer来进行传输 */
    master->transfer = spi_vir_master_transfer;

    /* 初始化中断下半部 */
    INIT_WORK(&svdd->ws, spi_vir_master_work);

    /* 初始化链表 */
    INIT_LIST_HEAD(&svdd->queue);

    /* 初始化锁 */
    spin_lock_init(&svdd->lock);
    
    /* 初始化等待队列 */
    init_waitqueue_head(&svdd->wait);

    /* 注册spi_master */
    ret = spi_register_master(master);
	if (ret < 0) {
        dev_err(NULL, "spi_register_master error.");
		spi_master_put(master);
		return ret;
	}

    return 0;
}

static int spi_vir_master_remove(struct platform_device *pdev)
{
    /* 反注册spi_master */
    struct spi_vir_driver_data *svdd = platform_get_drvdata(pdev);
    spi_master_put(svdd->master);
    flush_work(&svdd->ws);
    
    return 0;
}

static const struct of_device_id spi_vir_master[] = {
    { .compatible = "jz2440,virtual_spi_master", },
    {},
};

static struct platform_driver spi_vir_master_driver = {
    .probe = spi_vir_master_probe,
    .remove = spi_vir_master_remove,
    .driver = {
        .name = "jz-spi-vir-master",
        .of_match_table = of_match_ptr(spi_vir_master),
    }
};

static int spi_vir_master_init(void)
{
    int ret = platform_driver_register(&spi_vir_master_driver);
    if (ret < 0) {
        dev_err(NULL, "spi_vir_master_init failed");
        return -EBUSY;
    }

    return 0;
}

static void spi_vir_master_exit(void)
{
    platform_driver_unregister(&spi_vir_master_driver);
}

module_init(spi_vir_master_init);
module_exit(spi_vir_master_exit);

MODULE_AUTHOR("huang");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("spi master driver");

// 参考kernel中的spi-sh.c


